﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk.Client;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using System.Xml;

namespace EWSEditor.CRM
{
    class CrmOrgMailboxCache
    {
        public static Entity TargetMailbox;
        
        public static bool TargetMailboxIsQueue;
        public static Entity Organization;
        public static Entity TargetUserSettings;
        public static Entity Queue;
        public static EntityReference LoggedInSystemUser;
        public static string TargetMailboxEmailAddress;
        public static bool UserOwnsTargetMailbox;
        public static Entity ServerProfile;
        public static bool SupportMode;
        public static Dictionary<string, string> OrgDBOrgSettings = new Dictionary<string, string>();

        private static Guid _mailboxId;
        public static Guid MailboxId
        {
            get
            {
                if (_mailboxId == Guid.Empty)
                {
                    _mailboxId = (Guid)TargetMailbox["mailboxid"];
                    return _mailboxId;
                }
                else
                {
                    return _mailboxId;
                }
            }
    
        }

        public static string TaskFolderId
        {
            get
            {
                if (_taskFolderId == null)
                {
                    PopulateFolderIds();
                }
                return _taskFolderId;
            }
            set
            {
                _taskFolderId = value;
            }
        }

        private static string _taskFolderId;
        public static string AppointmentFolderId
        {
            get
            {
                if (_appointmentFolderId == null)
                {
                    PopulateFolderIds();
                }
                return _appointmentFolderId;
            }
            set
            {
                _appointmentFolderId = value;
            }
        }

        private static string _appointmentFolderId;
        public static string ContactFolderId
        {
            get
            {
                if (_contactFolderId == null)
                {
                    PopulateFolderIds();
                }
                return _contactFolderId;
            }
            set
            {
                _contactFolderId = value;
            }
        }
        private static string _contactFolderId;
        private static void PopulateFolderIds()
        {
            string xml = ExchangeSyncStateXml.GetCurrentExchangeSyncStateXml();
            List<ExchangeFolderSyncState> folderStates = ExchangeSyncStateXml.GetExchangeFolderSyncStateCollectionFromXpath("ExchangeSyncState/LastFolderItemsSyncStates", xml);

            foreach(ExchangeFolderSyncState folder in folderStates)
            {
                if (folder.FolderName == "Contact")
                {
                    _contactFolderId = folder.FolderId;
                    continue;
                }

                if (folder.FolderName == "Task")
                {
                    _taskFolderId = folder.FolderId;
                    continue;
                }
                if (folder.FolderName == "Appointment")
                {
                    _appointmentFolderId = folder.FolderId;
                    continue;
                }
            }
        }
        public static Guid SubscriptionId
        {
            get
            {
                if (_subscriptionId == Guid.Empty)
                {
                    _subscriptionId = GetSubscriptionId();
                    return _subscriptionId;
                }
                return _subscriptionId;
            }
            set
            {
                _subscriptionId = value;
            }
        }

        private static Guid _subscriptionId;

        private static Guid GetSubscriptionId()
        {
            string SyncXml = ExchangeSyncStateXml.GetCurrentExchangeSyncStateXml();

            if (SyncXml == null)
            {
                return Guid.Empty;
            }

            ExchangeSyncStateXml syncState = new ExchangeSyncStateXml(SyncXml);
            return syncState.SubscriptionId;
            
            
            //QueryByAttribute query = new QueryByAttribute("subscription");
            //query.ColumnSet = new ColumnSet();
            //query.Attributes.AddRange("systemuserid");
            //query.Values.AddRange(LoggedInSystemUser.Id);
            //query.Attributes.AddRange("subscriptiontype");
            //query.Values.AddRange(1);
            //EntityCollection subscriptions = CrmAuthInfoFactory.GetOrgProxy().RetrieveMultiple(query);

            //if (subscriptions.Entities.Count > 1)
            //{
            //    throw new Exception("Found more than 1 active subscription.");
            //}
            //if (subscriptions.Entities.Count == 0)
            //{
            //    throw new Exception("Did not find an active subscription associated with the user.");
            //}

            //return subscriptions[0].Id;


        }

        internal static void DoesUserOwnMailbox(Entity mailbox)
        {
            if (LoggedInSystemUser != null)
            {
                if (((EntityReference)mailbox["regardingobjectid"]).Id == LoggedInSystemUser.Id)
                {
                    UserOwnsTargetMailbox = true;
                    return;
                }
                UserOwnsTargetMailbox = false;
                return;
            }
            else
            {
                UserOwnsTargetMailbox = false;
            }
        }

        internal static void SetLoggedInUser(OrganizationServiceProxy orgProxy)
        {
            if (TargetMailboxIsQueue)
            { return; }

            Guid systemuserid = ((WhoAmIResponse)orgProxy.Execute(new WhoAmIRequest())).UserId;
            LoggedInSystemUser = new EntityReference("systemuser", systemuserid);

        }

        internal static void RetrieveOrganization(OrganizationServiceProxy orgProxy)
        {
            QueryByAttribute query = new QueryByAttribute("organization");
            query.ColumnSet = new ColumnSet(new string[] {"defaultemailserverprofileid", "emailconnectionchannel", "emailcorrelationenabled", "featureset", "hashdeltasubjectcount", "hashfilterkeywords", "hashmaxcount", "hashminaddresscount", "ignoreinternalemail", "incomingemailexchangeemailretrievalbatchsize", "isemailserverprofilecontentfilteringenabled", "ismailboxforcedunlockingenabled", "ismailboxinactivebackoffenabled", "mailboxintermittentissueminrange", "mailboxpermanentissueminrange", "maxfolderbasedtrackingmappings", "maximumtrackingnumber", "maxverboseloggingmailbox", "maxverboseloggingsynccycles", "minofflinesyncinterval", "minoutlooksyncinterval", "nexttrackingnumber", "orgdborgsettings", "trackingprefix", "trackingtokenidbase", "trackingtokeniddigits", "defaultemailsettings", "defaultmobileofflineprofileid", "enablesmartmatching", "expiresubscriptionsindays", "isassignedtaskssyncenabled", "isappointmentattachmentsyncenabled", "iscontactmailingaddresssyncenabled", "ismobileofflineenabled", "maxconditionsformobileofflinefilters", "mobileofflinesyncinterval", "officeappsautodeploymentenabled", "dayssincerecordlastmodifiedmaxvalue"});
            query.Attributes.AddRange("isdisabled");
            query.Values.AddRange(false);
            EntityCollection organizations = orgProxy.RetrieveMultiple(query);

            if (organizations.Entities.Count < 1)
            {
                throw new Exception("Did not find an active Dynamics 365 Organization associated with the Organization Service.");
            }

            if (organizations.Entities.Count > 1)
            {
                throw new Exception("More than 1 Organization record was found.");
            }
          
            Organization = organizations[0];

            SetOrgDBOrgSettings();

        }

        public static void SetOrgDBOrgSettings()
        {
            if (null == Organization)
            {
                throw new Exception("Organization cache is not set.");
            }

            if (!Organization.Contains("orgdborgsettings"))
            {
                return;
            }

            OrgDBOrgSettings.Clear(); //Reset the Dictionary

            string xml = (string)Organization["orgdborgsettings"];

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);
            string xPath = "OrgSettings";
            var nodes = xmlDoc.SelectNodes(xPath);
            ParseXmlAndSetDictionary(nodes);
        }

        private static void ParseXmlAndSetDictionary(XmlNodeList nodes)
        {
            foreach(XmlNode node in nodes)
            {
                foreach(XmlNode node1 in node)
                {
                    OrgDBOrgSettings.Add(node1.Name, node1.InnerText);
                }
            }
        }

        internal static void RetrieveUserOrQueueSettings(OrganizationServiceProxy orgProxy)
        {

            if (!TargetMailboxIsQueue)
            {
                Guid systemuserid;

                if (!UserOwnsTargetMailbox)
                {
                    systemuserid = ((EntityReference)TargetMailbox["regardingobjectid"]).Id;
                }
                else
                {
                    systemuserid = LoggedInSystemUser.Id;
                }

                QueryByAttribute userquery = new QueryByAttribute("usersettings");
                userquery.ColumnSet = new ColumnSet(new string[] { "autocreatecontactonpromote", "incomingemailfilteringmethod", "nexttrackingnumber", "trackingtokenid" });
                userquery.Attributes.AddRange("systemuserid");
                userquery.Values.AddRange(systemuserid);
                EntityCollection usersettingscollection = orgProxy.RetrieveMultiple(userquery);

                if (usersettingscollection.Entities.Count < 1)
                {
                    throw new Exception("Did not find an active Dynamics 365 Organization associated with the Organization Service.");
                }

                if (usersettingscollection.Entities.Count > 1)
                {
                    throw new Exception("More than 1 Organization record was found.");
                }

                TargetUserSettings = usersettingscollection[0];
                return;
            }

            //Support users do not have access to Queue.
            if (SupportMode)
            {
                return;
            }

            Queue = orgProxy.Retrieve("queue", ((EntityReference)TargetMailbox["regardingobjectid"]).Id, new ColumnSet(new string[] { "emailrouteraccessapproval", "incomingemaildeliverymethod", "incomingemailfilteringmethod", "isemailaddressapprovedbyo365admin", }));
            return;
        }

        internal static void RefreshCache()
        {
            OrganizationServiceProxy orgProxy = CrmAuthInfoFactory.GetOrgProxy();
            SetLoggedInUser(orgProxy);
            RetrieveOrganization(orgProxy);
            RetrieveUserOrQueueSettings(orgProxy);
            RetrieveAndSetTargetMailbox(orgProxy);
            RetrieveAndSetServerProfile(orgProxy);
        }

        //Retrieves a mailbox with all columns from the organization service proxy.
        internal static void RetrieveAndSetTargetMailbox(OrganizationServiceProxy orgProxy)
        {
            QueryByAttribute query = new QueryByAttribute("mailbox");
            query.ColumnSet = new ColumnSet() { AllColumns = true };
            query.Attributes.AddRange("emailaddress");
            query.Values.AddRange(TargetMailboxEmailAddress);
            query.Attributes.AddRange("statecode");
            query.Values.AddRange(0);
            EntityCollection mailboxes = orgProxy.RetrieveMultiple(query);
            TargetMailbox = mailboxes[0];
        }

        public static void RetrieveAndSetServerProfile(OrganizationServiceProxy orgProxy)
        {
            //When signing in as support user, if the configuration details are accessed
            //before the mailbox details, the emailserverprofile attribute will not be set. 
            if (!TargetMailbox.Attributes.Contains("emailserverprofile"))
            {
                RetrieveAndSetTargetMailbox(orgProxy);
            }

            ServerProfile = orgProxy.Retrieve("emailserverprofile", ((EntityReference)TargetMailbox["emailserverprofile"]).Id, new ColumnSet() { AllColumns = true } );
        }

        public static void InvalidateCache()
        {
            TargetMailbox = null;
            Organization = null;
            TargetUserSettings = null;
            Queue = null;
            LoggedInSystemUser = null;
            TargetMailboxEmailAddress = null;
            ServerProfile = null;
    }

    }
}
